/*
 * Copyright (c) 2003, the JUNG Project and the Regents of the University
 * of California
 * All rights reserved.
 *
 * This software is open-source under the BSD license; see either
 * "license.txt" or
 * http://jung.sourceforge.net/license.txt for a description.
 */
package visualization;

import edu.uci.ics.jung.algorithms.layout.AbstractLayout;
import edu.uci.ics.jung.algorithms.layout.GraphElementAccessor;
import edu.uci.ics.jung.algorithms.layout.RadiusGraphElementAccessor;
import edu.uci.ics.jung.algorithms.layout.util.RandomLocationTransformer;
import edu.uci.ics.jung.algorithms.util.IterativeContext;
import edu.uci.ics.jung.graph.Graph;

//import edu.uci.ics.jung.graph.AbstractGraph;
//import edu.uci.ics.jung.graph.util.EdgeType;
//import edu.uci.ics.jung.graph.util.Pair;
//import edu.uci.ics.jung.algorithms.layout.Layout;
//import edu.uci.ics.jung.visualization.BasicVisualizationServer;
//import edu.uci.ics.jung.visualization.VisualizationViewer;
//import edu.uci.ics.jung.visualization.control.CrossoverScalingControl;
//import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse;
//import edu.uci.ics.jung.visualization.control.ModalGraphMouse;
//import edu.uci.ics.jung.visualization.control.ScalingControl;
//import edu.uci.ics.jung.visualization.renderers.Renderer.VertexLabel.Position;

import optimizers.ganeat.Connection;
import optimizers.ganeat.Individual;

import org.apache.commons.collections15.Factory;
import org.apache.commons.collections15.map.LazyMap;

import java.awt.Dimension;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

/**
 * Implements a self-organizing map layout algorithm, based on Meyer's
 * self-organizing graph methods.
 * 
 * @author Yan Biao Boey
 */
public class OligoISOMLayout<V, E> extends AbstractLayout<V, E> implements
		IterativeContext {

	Map<V, ISOMVertexData> isomVertexData = LazyMap.decorate(
			new HashMap<V, ISOMVertexData>(), new Factory<ISOMVertexData>() {
				public ISOMVertexData create() {
					return new ISOMVertexData();
				}
			});

	private int maxEpoch;
	private int epoch;

	private double adaption;
	private double initialAdaption;
	private double minAdaption;

	// Electric Spring////////
	private int progress = 0;
	private int l = 0;
	private int P = 0;
	private int S = 500;
	private ArrayList<Double> x0 = new ArrayList<Double>();
	private ArrayList<Double> x1 = new ArrayList<Double>();
	private ArrayList<Double> y0 = new ArrayList<Double>();
	private ArrayList<Double> y1 = new ArrayList<Double>();
	private ArrayList<Double> Fx = new ArrayList<Double>();
	private ArrayList<Double> Fy = new ArrayList<Double>();
	private double E = 999999999;
	private double E0 = 0;
	private double Emin = 999999999;
	private double step = 60; // step for coordinates update
	private boolean Conv = false;
	private int Deg = 0;
	private double K = 0;
	private double Kr = 0;
	private int Try = 1;
	private double K2 = 0;
	private int Retry = 0;
	private double tolerance = 0.8;
	private static int maxRetry = 5;
	private static double toleranceDown = 0.9;
	private Map<String, java.awt.geom.Point2D> locs;
	// /////////////////////////

	protected GraphElementAccessor<V, E> elementAccessor = new RadiusGraphElementAccessor<V, E>();

	private double coolingFactor;

	private List<V> queue = new ArrayList<V>();
	private String status = null;

	/**
	 * Returns the current number of epochs and execution status, as a string.
	 */
	public String getStatus() {
		return status;
	}

	/**
	 * Creates an <code>ISOMLayout</code> instance for the specified graph
	 * <code>g</code>.
	 * 
	 * @param g
	 */
	public OligoISOMLayout(Graph<V, E> g) {
		super(g);
		this.locs = null;
	}

	public OligoISOMLayout(Graph<V, E> g,
			Map<String, java.awt.geom.Point2D> locs) {
		super(g);
		this.locs = locs;
	}

	public void initialize() {
		setInitializer(new RandomLocationTransformer<V>(new Dimension(
				(int) Math.max(180, getSize().getWidth()), (int) Math.max(180,
						getSize().getHeight()))));
		maxEpoch = 2000;
		epoch = 1;

		initialAdaption = 90.0D / 100.0D;
		adaption = initialAdaption;
		minAdaption = 0;

	}

	/**
	 * Advances the current positions of the graph elements.
	 */
	public void step() {
		if (locs == null) {
			status = "epoch: " + epoch + "; ";
			if (epoch < maxEpoch) {
				status += " status: running";
				if (!Conv)
					optimizeGraph();
				if (Conv)
					epoch = maxEpoch;

			} else {
				status += "adaption: " + adaption + "; ";
				status += "status: done";
				// done = true;
			}
		} else {
			Random rand = new Random();
			for (V v : getGraph().getVertices()) {
				Point2D p = locs.get(v);
				if (p != null) {
					int dx = rand.nextInt(5) - 2;
					int dy = rand.nextInt(5) - 2;
					setLocation(v, p.getX() + dx, p.getY() + dy);
				}
			}
		}

	}

	protected ISOMVertexData getISOMVertexData(V v) {
		return isomVertexData.get(v);
	}

	/**
	 * This one is an incremental visualization.
	 * 
	 * @return <code>true</code> is the layout algorithm is incremental,
	 *         <code>false</code> otherwise
	 */
	public boolean isIncremental() {
		return true;
	}

	/**
	 * Returns <code>true</code> if the vertex positions are no longer being
	 * updated. Currently <code>ISOMLayout</code> stops updating vertex
	 * positions after a certain number of iterations have taken place.
	 * 
	 * @return <code>true</code> if the vertex position updates have stopped,
	 *         <code>false</code> otherwise
	 */
	public boolean done() {
		return epoch >= maxEpoch;
	}

	protected static class ISOMVertexData {
		int distance;
		boolean visited;

		protected ISOMVertexData() {
			distance = 0;
			visited = false;
		}
	}

	/**
	 * Resets the layout iteration count to 0, which allows the layout algorithm
	 * to continue updating vertex positions.
	 */
	public void reset() {
		epoch = 0;
	}

	public void energyComputing() {

		double C = 2; // regulates the relative strength of the attractive and
						// repulsive forces
		double p = 0; // the larger p, the weaker the long range repulsive
						// forces
		double q = 0; // the larger q, the stronger attractive forces
		double tol = 0.5; // tolerance parameter on convergence
		// epoch++;

		// reset energy
		E0 = E;
		E = 0;
		l++;

		// System.err.println("Iteration " + (l));
		if (l > 0) {

			int i = 0;
			for (V v1 : getGraph().getVertices()) {

				// coordinates variables
				Fx.set(i, 0.0);
				Fy.set(i, 0.0);
				x0.set(i, x1.get(i));
				y0.set(i, y1.get(i));
				x1.set(i, getX(v1));
				y1.set(i, getY(v1));

				// Deal with inhibition sequences
				String VertName = v1.toString();
				String FirstPart = ".";
				String SecondPart = ".";
				if (VertName.startsWith("I")) {
					String VertNameI = VertName.substring(1);
					int L = VertNameI.length();
					int e = 1;
					int e1 = 1;
					// System.err.println("Inhibiting vertex " + (i+1) +
					// " :     I" + VertNameI + "    L = " + L);
					while (e < L) {
						for (V v2 : getGraph().getVertices()) {
							String Vert2Name = v2.toString();
							if (Vert2Name.equals(VertNameI.substring(L - e))) {
								SecondPart = VertNameI.substring(L - e);
								e1++;
							}
						}
						// System.err.println(" Second part : " + SecondPart);
						e++;
					}
					FirstPart = VertNameI.substring(0, L - (e1 - 1));
					// System.err.println(" First part :  " + FirstPart);
					// System.err.println("Vertex " + v1.toString() + " :      "
					// + VertName + " :  inhibits " + FirstPart + " and " +
					// SecondPart );
				}

				for (V v2 : getGraph().getVertices()) {

					String Vert2Name = v2.toString();
					double x2 = getX(v2);
					double y2 = getY(v2);
					double r = Math.pow(
							Math.pow((x2 - x1.get(i)), 2)
									+ Math.pow((y2 - y1.get(i)), 2), 0.5);

					if (v1 != v2) {

						// create repulsion force between each vertexes
						double Frx = Math.pow(K / r, 2.5 + p) * (-C)
								* ((x2 - x1.get(i)) / r);
						double Fry = Math.pow(K / r, 2.5 + p) * (-C)
								* ((y2 - y1.get(i)) / r);
						Fx.set(i, Fx.get(i) + Frx);
						Fy.set(i, Fy.get(i) + Fry);

						// if(!v1.toString().startsWith("C"))
						// System.err.println("Vertex " + v1.toString() + " [" +
						// (int)getX(v1) + "," + (int)getY(v1) + "] " +
						// " repulsed by " + v2.toString() + " [" +
						// (int)getX(v2) + "," + (int)getY(v2) + "]          " +
						// "with force Frx = " + Frx + ", Fry = " + Fry +
						// "   r = " + r);

						if ((getGraph().findEdge(v1, v2) != null || getGraph()
								.findEdge(v2, v1) != null)
								|| (Vert2Name.equals(FirstPart) || Vert2Name
										.equals(SecondPart))) {
							double Fax = Math.pow(r / K, 2 + q)
									* ((x2 - x1.get(i)) / r);
							double Fay = Math.pow(r / K, 2 + q)
									* ((y2 - y1.get(i)) / r);
							Fx.set(i, Fx.get(i) + Fax);
							Fy.set(i, Fy.get(i) + Fay);

							// if(!v1.toString().startsWith("C"))
							// System.err.println("Vertex " + v1.toString() +
							// " [" + (int)getX(v1) + "," + (int)getY(v1) + "] "
							// +
							// " attracted by " + v2.toString() + " [" +
							// (int)getX(v2) + "," + (int)getY(v2) +
							// "]         " +
							// "with force Fax = " + Fax + ", Fay = " + Fay +
							// "   r = " + r);
						}
					}
				}

				// repulsing force of the walls
				double r = getX(v1);
				double Fm = Math.pow((K / 4) / r, 1) * (-C)
						* ((0 - x1.get(i)) / r) /*
												 * + Math.pow( r / (K/2), 1 ) *
												 * ( ( 0 - x1.get(i) ) / r )
												 */;
				r = getSize().getWidth() - getX(v1);
				Fm += Math.pow((K / 4) / r, 1) * (-C)
						* ((getSize().getWidth() - x1.get(i)) / r) /*
																	 * +
																	 * Math.pow(
																	 * r /
																	 * (K/2), 2
																	 * ) * ( (
																	 * getSize
																	 * ().
																	 * getWidth
																	 * () -
																	 * x1.get(i)
																	 * ) / r )
																	 */;
				Fx.set(i, Fx.get(i) + Fm);
				r = getY(v1);
				Fm = Math.pow((K / 4) / r, 1) * (-C) * ((0 - y1.get(i)) / r) /*
																			 * +
																			 * Math
																			 * .
																			 * pow
																			 * (
																			 * r
																			 * /
																			 * (
																			 * K
																			 * /
																			 * 2
																			 * )
																			 * ,
																			 * 1
																			 * )
																			 * *
																			 * (
																			 * (
																			 * 0
																			 * -
																			 * y1
																			 * .
																			 * get
																			 * (
																			 * i
																			 * )
																			 * )
																			 * /
																			 * r
																			 * )
																			 */;
				r = getSize().getHeight() - getY(v1);
				Fm += Math.pow((K / 4) / r, 1) * (-C)
						* ((getSize().getHeight() - y1.get(i)) / r) /*
																	 * +
																	 * Math.pow(
																	 * r /
																	 * (K/2), 2
																	 * ) * ( (
																	 * getSize
																	 * ().
																	 * getHeight
																	 * () -
																	 * y1.get(i)
																	 * ) / r )
																	 */;
				Fy.set(i, Fy.get(i) + Fm);

				// Energy computing
				double e = (Fx.get(i) * Fx.get(i)) + (Fy.get(i) * Fy.get(i));
				E += e;

				// modify vertex position
				double V = 3.0; // evolution speed factor
				double X = x1.get(i) + Math.min(V * Abs(Fx.get(i)), step)
						* (Fx.get(i) / Abs(Fx.get(i)));
				double Y = y1.get(i) + Math.min(V * Abs(Fy.get(i)), step)
						* (Fy.get(i) / Abs(Fy.get(i)));
				if (X < getSize().getWidth() - 10 && X > 10)
					x1.set(i, X);
				if (Y < getSize().getHeight() - 10 && Y > 10)
					y1.set(i, Y);
				setLocation(v1, x1.get(i), y1.get(i));

				i++;
			}

			// update step
			step = updateStep(step, E0, E);

			// test convergence
			// System.err.println("Resulsts of computation :");
			// System.err.println(" -> E0 = " + (int)E0 + ", E = " + (int)E +
			// ", (K = " + (int)K + ")");
			// System.err.println(" -> Updated step : " + step + " (porgress " +
			// progress + ")");
			Conv = true;
			int n = 0;
			for (V v : getGraph().getVertices()) {
				double D = Abs(Math.pow(x1.get(n) - x0.get(n), 2)
						+ Math.pow(y1.get(n) - y0.get(n), 2));
				if (D >= tol)
					Conv = false;
				n++;
			}

			// if(E < Emin)
			// Conv = true;
			// System.err.println(" ");

		}
		if (l > S) {
			resetElectricSpring();
			Try++;
			S += S * 1.2;
			// System.err.println("Try " + Try + "...");
		}

		if (Conv) {
			//System.err.println("Graph energy minimized after " + l
					//+ " iterations. ( E = " + E + " )");
		}

	}

	public double updateStep(double step, double E0, double E) {
		double t = 0.98;

		if (E < E0) {
			progress++;
			if (progress > 2) {
				progress = 0;
				step = step / t;
			}
		} else {
			progress = 0;
			step = step * t;
		}

		return step;
	}

	public double Abs(double x) {
		double x1 = 0;
		if (x > 0)
			x1 = x;
		else
			x1 = -x;
		return x1;
	}

	public int Floor(double x) {
		int x1 = (int) x;
		return x1;
	}

	public int initElectricSpring() {

		progress = 0;
		l = 0;
		E = 999999999;
		E0 = 0;
		step = Math.pow(
				Math.pow(getSize().getHeight() / 4, 2)
						+ Math.pow(getSize().getWidth() / 4, 2), 0.5); // step
																		// for
																		// coordinates
																		// update

		// initialization coordinates arrays
		int m = 0;
		int Deg = 0;
		for (V v : getGraph().getVertices()) {
			Fx.add(0.0);
			Fy.add(0.0);
			x0.add(0.0);
			y0.add(0.0);
			if (m == 0) {
				x1.add(getSize().getWidth() / 2);
				y1.add(getSize().getHeight() / 2);
			}
			if (m != 0) {
				x1.add((Math.random()
						* (getSize().getWidth() - getSize().getWidth() / 2.5) + getSize()
						.getWidth() / 5));
				y1.add((Math.random()
						* (getSize().getHeight() - getSize().getHeight() / 2.5) + getSize()
						.getWidth() / 5));
			}
			setLocation(v, x1.get(m), y1.get(m));
			m++;
			Deg += getGraph().degree(v);
		}
		// System.err.println(m + " vertices to optimize, degree = " + Deg );

		// determine optimal distance K
		double A = getSize().getHeight() * getSize().getWidth();
		double C = 0.9;
		K = Math.pow(C * C * A / m, 0.5);
		if (K > getSize().getHeight() / 5)
			K = getSize().getHeight() / 5;

		// Dertermine min convergence energy
		Emin = m;

		return Deg;
	}

	public int resetElectricSpring() {

		progress = 0;
		E = 999999999;
		E0 = 0;
		step = Math.pow(
				Math.pow(getSize().getHeight() / 4, 2)
						+ Math.pow(getSize().getWidth() / 4, 2), 0.5); // step
																		// for
																		// coordinates
																		// update

		// initialization coordinates arrays
		int m = 0;
		int Deg = 0;
		for (V v : getGraph().getVertices()) {
			Fx.set(m, 0.0);
			Fy.set(m, 0.0);
			x0.set(m, 0.0);
			y0.set(m, 0.0);
			if (m == 0) {
				x1.set(m, getSize().getWidth() / 2);
				y1.set(m, getSize().getHeight() / 2);
			}
			if (m != 0) {
				x1.set(m,
						(Math.random()
								* (getSize().getWidth() - getSize().getWidth() / 2.5) + getSize()
								.getWidth() / 5));
				y1.set(m,
						(Math.random()
								* (getSize().getHeight() - getSize()
										.getHeight() / 2.5) + getSize()
								.getWidth() / 5));
			}
			setLocation(v, x1.get(m), y1.get(m));
			m++;
			Deg += getGraph().degree(v);
		}
		// System.err.println(m + " vertices detected, degree = " + Deg );

		return Deg;
	}

	public void centerGraph() {
		double Xm = 0;
		double Ym = 0;
		int n = 0;
		for (V v : getGraph().getVertices()) {
			Xm += getX(v);
			Ym += getY(v);
			n++;
		}
		Xm = Xm / n;
		Ym = Ym / n;
		// System.err.println("Xm = " + Xm + ", Ym = " + Ym);
		Xm = getSize().getWidth() / 2 - Xm;
		Ym = getSize().getHeight() / 2 - Ym;
		for (V v : getGraph().getVertices()) {
			if (getX(v) < 0.9 * getSize().getWidth()
					&& getX(v) > 0.1 * getSize().getHeight()
					&& getY(v) < 0.9 * getSize().getHeight()
					&& getY(v) > 0.1 * getSize().getHeight())
				setLocation(v, getX(v) + Xm, getY(v) + Ym);
		}
	}

	public boolean distanceCheck(double tol) {

		boolean Dist = true;
		for (V v1 : getGraph().getVertices()) {
			for (V v2 : getGraph().getVertices()) {
				if (v1 != v2) {
					double dx = getX(v2) - getX(v1);
					double dy = getY(v2) - getY(v1);
					double r2 = dx * dx + dy * dy;
					if (r2 < K2 * K2 * tol) {
						Dist = false;
						Retry++;
						if (r2 < Kr)
							Kr = r2;
					}
				}
			}
		}
		return Dist;
	}

	public void optimizeGraph() {

		S = 100;
		Deg = initElectricSpring();
		Conv = false;
		boolean Dist = false;

		while (!Dist) {
			while (!Conv) {
				energyComputing();
			}
			Kr = 99999999;
			if (Retry == OligoISOMLayout.maxRetry) {
				Retry = 0;
				System.err
						.println("Too difficult! Reduction of distance tolerance factor "
								+ tolerance
								+ " -> "
								+ tolerance
								* OligoISOMLayout.toleranceDown);
				tolerance *= OligoISOMLayout.toleranceDown;
			}
			Dist = distanceCheck(tolerance);
			l = 0;
			Try = 1;
			S = 100;
			if (!Dist) {
				Conv = false;
				System.err.println("Nodes are too packed! Redrawing...");
				Deg = resetElectricSpring();
			}
		}

		centerGraph();
	}

}

// /*
// * Copyright (c) 2003, the JUNG Project and the Regents of the University
// * of California
// * All rights reserved.
// *
// * This software is open-source under the BSD license; see either
// * "license.txt" or
// * http://jung.sourceforge.net/license.txt for a description.
// */
// package visualization;
//
// import edu.uci.ics.jung.algorithms.layout.AbstractLayout;
// import edu.uci.ics.jung.algorithms.layout.GraphElementAccessor;
// import edu.uci.ics.jung.algorithms.layout.RadiusGraphElementAccessor;
// import edu.uci.ics.jung.algorithms.layout.util.RandomLocationTransformer;
// import edu.uci.ics.jung.algorithms.util.IterativeContext;
// import edu.uci.ics.jung.graph.Graph;
//
// import org.apache.commons.collections15.Factory;
// import org.apache.commons.collections15.map.LazyMap;
//
// import java.awt.Dimension;
// import java.awt.geom.Point2D;
// import java.util.ArrayList;
// import java.util.Collection;
// import java.util.ConcurrentModificationException;
// import java.util.HashMap;
// import java.util.List;
// import java.util.Map;
//
// /**
// * Implements a self-organizing map layout algorithm, based on Meyer's
// * self-organizing graph methods.
// *
// * @author Yan Biao Boey
// */
// public class OligoISOMLayout<V, E> extends AbstractLayout<V, E> implements
// IterativeContext {
//
// Map<V, ISOMVertexData> isomVertexData = LazyMap.decorate(
// new HashMap<V, ISOMVertexData>(), new Factory<ISOMVertexData>() {
// public ISOMVertexData create() {
// return new ISOMVertexData();
// }
// });
//
// private int maxEpoch;
// private int epoch;
//
// private int radiusConstantTime;
// private int radius;
// private int minRadius;
//
// private double adaption;
// private double initialAdaption;
// private double minAdaption;
//
// protected GraphElementAccessor<V, E> elementAccessor = new
// RadiusGraphElementAccessor<V, E>();
//
// private double coolingFactor;
//
// private List<V> queue = new ArrayList<V>();
// private String status = null;
//
// /**
// * Returns the current number of epochs and execution status, as a string.
// */
// public String getStatus() {
// return status;
// }
//
// /**
// * Creates an <code>ISOMLayout</code> instance for the specified graph
// * <code>g</code>.
// *
// * @param g
// */
// public OligoISOMLayout(Graph<V, E> g) {
// super(g);
// }
//
// public void initialize() {
// setInitializer(new RandomLocationTransformer<V>(new Dimension(
// (int) Math.max(180, getSize().getWidth()), (int) Math.max(180,
// getSize().getHeight()))));
// maxEpoch = 2000;
// epoch = 1;
//
// radiusConstantTime = 100;
// radius = 5;
// minRadius = 1;
//
// initialAdaption = 90.0D / 100.0D;
// adaption = initialAdaption;
// minAdaption = 0;
//
// // factor = 0; //Will be set later on
// coolingFactor = 2;
//
// // temperature = 0.03;
// // initialJumpRadius = 100;
// // jumpRadius = initialJumpRadius;
//
// // delay = 100;
// }
//
// /**
// * Advances the current positions of the graph elements.
// */
// public void step() {
// status = "epoch: " + epoch + "; ";
// if (epoch < maxEpoch) {
// adjust();
// updateParameters();
// status += " status: running";
//
// } else {
// status += "adaption: " + adaption + "; ";
// status += "status: done";
// // done = true;
// }
// }
//
// private synchronized void adjust() {
// // Generate random position in graph space
// Point2D tempXYD = new Point2D.Double();
//
// // creates a new XY data location
// tempXYD.setLocation(
// 10 + Math.random() * Math.max(180, getSize().getWidth()), 10
// + Math.random() * Math.max(180, getSize().getHeight()));
//
// // Get closest vertex to random position
// V winner = elementAccessor.getVertex(this, tempXYD.getX(),
// tempXYD.getY());
//
// while (true) {
// try {
// for (V v : getGraph().getVertices()) {
// ISOMVertexData ivd = getISOMVertexData(v);
// ivd.distance = 0;
// ivd.visited = false;
// }
// break;
// } catch (ConcurrentModificationException cme) {
// }
// }
// adjustVertex(winner, tempXYD);
// }
//
// private synchronized void updateParameters() {
// epoch++;
// double factor = Math.exp(-1 * coolingFactor * (1.0 * epoch / maxEpoch));
// adaption = Math.max(minAdaption, factor * initialAdaption);
// // jumpRadius = (int) factor * jumpRadius;
// // temperature = factor * temperature;
// if ((radius > minRadius) && (epoch % radiusConstantTime == 0)) {
// radius--;
// }
// }
//
// private synchronized void adjustVertex(V v, Point2D tempXYD) {
// queue.clear();
// ISOMVertexData ivd = getISOMVertexData(v);
// ivd.distance = 0;
// ivd.visited = true;
// queue.add(v);
// V current;
//
// while (!queue.isEmpty()) {
// current = queue.remove(0);
// ISOMVertexData currData = getISOMVertexData(current);
// Point2D currXYData = transform(current);
//
// double dx = tempXYD.getX() - currXYData.getX();
// double dy = tempXYD.getY() - currXYData.getY();
// double factor = adaption / Math.pow(2, currData.distance);
//
// currXYData.setLocation(currXYData.getX() + (factor * dx),
// currXYData.getY() + (factor * dy));
//
// if (currData.distance < radius) {
// Collection<V> s = getGraph().getNeighbors(current);
// while (true) {
// try {
// for (V child : s) {
// ISOMVertexData childData = getISOMVertexData(child);
// if (childData != null && !childData.visited) {
// childData.visited = true;
// childData.distance = currData.distance + 1;
// queue.add(child);
// }
// }
// break;
// } catch (ConcurrentModificationException cme) {
// }
// }
// }
// }
// }
//
// protected ISOMVertexData getISOMVertexData(V v) {
// return isomVertexData.get(v);
// }
//
// /**
// * This one is an incremental visualization.
// *
// * @return <code>true</code> is the layout algorithm is incremental,
// * <code>false</code> otherwise
// */
// public boolean isIncremental() {
// return true;
// }
//
// /**
// * Returns <code>true</code> if the vertex positions are no longer being
// * updated. Currently <code>ISOMLayout</code> stops updating vertex
// * positions after a certain number of iterations have taken place.
// *
// * @return <code>true</code> if the vertex position updates have stopped,
// * <code>false</code> otherwise
// */
// public boolean done() {
// return epoch >= maxEpoch;
// }
//
// protected static class ISOMVertexData {
// int distance;
// boolean visited;
//
// protected ISOMVertexData() {
// distance = 0;
// visited = false;
// }
// }
//
// /**
// * Resets the layout iteration count to 0, which allows the layout algorithm
// * to continue updating vertex positions.
// */
// public void reset() {
// epoch = 0;
// }
// }